#ifndef EXP_SELECT_HPP
#define EXP_SELECT_HPP

#include<type_traits>
#include<utility>

#include"exp_literal_types.hpp"

namespace exp_utilities {
    namespace exp_select_detail {
        template<int I, class T> struct select_type {
            using type = literal_types::no_exist_type;
        };

        template<int I, template<typename ...> typename TL, class First, class ... Rest>
        struct select_type<I, TL<First, Rest...>> {
            using type = typename select_type<I - 1, TL<Rest...>>::type;
        };

        template<template<typename ...> typename TL, class First, class ... Rest>
        struct select_type<0, TL<First, Rest...>> {
            using type = First;
        };

        template<int I, template<class...> class TL>
        struct select_type<I, TL<>> {
            using type = literal_types::no_exist_type;
            static_assert(false, "Index out of bounds for exp_select");
        };
    }

    template<std::size_t I, class TL>
    using exp_select = typename exp_select_detail::select_type<I, TL>::type;

    namespace exp_list_select_detail {
        template<std::size_t ...I>
        struct list_select_impl {
            template<template<typename ...>typename TL, class ...Tys>
            static constexpr TL<typename exp_select<I, TL<Tys...>>...> apply_impl(TL<Tys...>) {
                return {};
            }

            template<typename TL>
            using apply = decltype(apply_impl(std::declval<TL>()));

        };
    }
    
    template<std::size_t ...I>
    struct exp_list_select {
        template<typename TL>
        using apply = typename exp_list_select_detail::list_select_impl<I...>::template apply<TL>;
    };

    template<class index_sequence_t>
    struct to_exp_list_select_t_impl;

    template<std::size_t ...I>
    struct to_exp_list_select_t_impl<std::index_sequence<I...>> {
        using type = exp_list_select<I...>;
    };

    template<class index_sequence_t>
    using to_exp_list_select_t = typename to_exp_list_select_t_impl<index_sequence_t>::type;
}

#endif // !EXP_SELECT_HPP
